package com.wang.springframework.aop.framework.autoproxy;

import com.wang.springframework.aop.*;
import com.wang.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import com.wang.springframework.aop.framework.ProxyFactory;
import com.wang.springframework.beans.BeansException;
import com.wang.springframework.beans.factory.BeanFactory;
import com.wang.springframework.beans.factory.BeanFactoryAware;
import com.wang.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import com.wang.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;

import java.util.Collection;

/**
 * @author wangjianhua
 * @Description 融入bean生命周期的自动代理创建者
 * @date 2021/9/3/003 14:28
 **/
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {

    private DefaultListableBeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (DefaultListableBeanFactory)beanFactory;
    }



    @Override
    public Object postProcessorBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessorAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    private boolean isInfrastructureClass(Class<?> beanClass){
        return Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass);
    }

    /**
     * 通过 beanFactory.getBeansOfType 获取 AspectJExpressionPointcutAdvisor 开始
     * 获取了 advisors 以后就可以遍历相应的 AspectJExpressionPointcutAdvisor 填充对应的属性信息，
     * 包括：目标对象、拦截方法、匹配器，之后返回代理对象即可。
     * 那么现在调用方获取到的这个 Bean 对象就是一个已经被切面注入的对象了，当调用方法的时候，则会被按需拦截，处理用户需要的信息。
     * @param beanClass bean class
     * @param beanName  bean 名称
     * @return 代理对象
     * @throws BeansException 异常
     */
    @Override
    public Object postProcessorBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if(isInfrastructureClass(beanClass)){
            return null;
        }
        Collection<AspectJExpressionPointcutAdvisor> advisors =
                beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();

        for (AspectJExpressionPointcutAdvisor advisor : advisors) {
            ClassFilter classFilter = advisor.getPointcut().getClassFilter();
            if(!classFilter.matches(beanClass)){
                continue;
            }

            AdvisedSupport advisedSupport = new AdvisedSupport();

            TargetSource targetSource = null;

            try
            {
                targetSource = new TargetSource(beanClass.getDeclaredConstructor().newInstance());
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }

            advisedSupport.setTargetSource(targetSource);
            advisedSupport.setMethodInterceptor((MethodInterceptor)advisor.getAdvice());
            advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
            advisedSupport.setProxyTargetClass(false);

            return new ProxyFactory(advisedSupport).getProxy();
        }
        return null;
    }
}
